﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;

namespace WebSocketDemo.Tools
{
    public static class ObjectExtensions
    {
        /// <summary>Indicates whether the specified object is null.</summary>
        /// <param name="obj">The object to test.</param>
        /// <returns>true if the object is null;otherwise, false.</returns>
        public static bool IsNull(this object obj)
        {
            return obj == null;
        }

        /// <summary>Indicates whether the specified object is not null.</summary>
        /// <param name="obj">The object to test.</param>
        /// <returns>true if the object is not null;otherwise, false.</returns>
        public static bool IsNotNull(this object obj)
        {
            return !obj.IsNull();
        }

        /// <summary>
        /// Indicates whether the specified object is <typeparamref name="T" />.
        /// </summary>
        /// <typeparam name="T">The type of expected.</typeparam>
        /// <param name="obj">The object to test.</param>
        /// <returns>true if the object is <typeparamref name="T" />;otherwise, false.</returns>
        public static bool Is<T>(this object obj) where T : class
        {
            return obj is T;
        }

        /// <summary>
        /// Indicates whether the specified object is not <typeparamref name="T" />.
        /// </summary>
        /// <typeparam name="T">The type of expected.</typeparam>
        /// <param name="obj">The object to test.</param>
        /// <returns>true if the object is not <typeparamref name="T" />;otherwise, false.</returns>
        public static bool IsNot<T>(this object obj) where T : class
        {
            return !obj.Is<T>();
        }

        /// <summary>
        /// Convert the specified object to <typeparamref name="T" />.
        /// </summary>
        /// <typeparam name="T">The type of expected.</typeparam>
        /// <param name="obj">The object to convert.</param>
        /// <returns>The converted <typeparamref name="T" /></returns>
        public static T As<T>(this object obj) where T : class
        {
            return obj as T;
        }

        /// <summary>
        /// Convert the <paramref name="obj" /> to byte[].
        /// </summary>
        /// <param name="obj">The object to convert.</param>
        /// <exception cref="T:System.Exception"></exception>
        /// <returns>The byte[] of <paramref name="obj" /></returns>
        public static byte[] ToBytes(this object obj)
        {
            byte[] numArray = (byte[])null;
            if (obj != null)
            {
                try
                {
                    using (MemoryStream memoryStream = new MemoryStream())
                    {
                        new BinaryFormatter().Serialize((Stream)memoryStream, obj);
                        numArray = memoryStream.ToArray();
                        memoryStream.Close();
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
            return numArray;
        }

        /// <summary>
        /// Convert the <paramref name="obj" /> to xml string.
        /// </summary>
        /// <param name="obj">The object to convert.</param>
        /// <exception cref="T:System.Exception"></exception>
        /// <returns>The xml string of the <paramref name="obj" />.</returns>
        public static string ToXml(this object obj)
        {
            string str = (string)null;
            if (obj == null)
                return str;
            try
            {
                using (StringWriter stringWriter = new StringWriter())
                {
                    new XmlSerializer(obj.GetType()).Serialize((TextWriter)stringWriter, obj);
                    return stringWriter.ToString();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Determines whether the <paramref name="item" /> in the specified <paramref name="list" />.
        /// </summary>
        /// <typeparam name="T">The type of <paramref name="item" />.</typeparam>
        /// <param name="item">The item.</param>
        /// <param name="list">The list.</param>
        /// <returns>
        ///   <c>true</c> if the <paramref name="item" /> is in the <paramref name="list" />; otherwise, <c>false</c>.
        /// </returns>
        /// <exception cref="T:System.ArgumentNullException">list</exception>
        public static bool In<T>(this T item, params T[] list)
        {
            if (list.IsNull())
                throw new ArgumentNullException(nameof(list));
            return ((IEnumerable<T>)list).Contains<T>(item);
        }

        /// <summary>
        /// Determines whether the <paramref name="actual" /> is between <paramref name="lower" /> and <paramref name="upper" />.
        /// </summary>
        /// <typeparam name="T">The type where inherts <see cref="T:System.IComparable`1" /></typeparam>
        /// <param name="actual">The actual.</param>
        /// <param name="lower">The lower.</param>
        /// <param name="upper">The upper.</param>
        /// <returns>
        ///   <c>true</c> <paramref name="actual" /> is between <paramref name="lower" /> and <paramref name="upper" />; otherwise, <c>false</c>.
        /// </returns>
        public static bool Between<T>(this T actual, T lower, T upper) where T : IComparable<T>
        {
            if (actual.CompareTo(lower) >= 0)
                return actual.CompareTo(upper) <= 0;
            return false;
        }

        /// <summary>Withes the specified action.</summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj">The object.</param>
        /// <param name="action">The action.</param>
        public static void With<T>(this T obj, Action<T> action)
        {
            action(obj);
        }

        /// <summary>
        /// To the specified value of <typeparamref name="T" />.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj">The object.</param>
        /// <returns></returns>
        public static T To<T>(this object obj)
        {
            return (T)Convert.ChangeType(obj, typeof(T));
        }

        /// <summary>
        /// To the specified value of <typeparamref name="T" />.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj">The object.</param>
        /// <param name="default">The default.</param>
        /// <returns></returns>
        public static T To<T>(this object obj, T @default)
        {
            try
            {
                return (T)Convert.ChangeType(obj, typeof(T));
            }
            catch (Exception ex)
            {
                return @default;
            }
        }

        /// <summary>Throws if argument is null.</summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj">The object.</param>
        /// <param name="parameterName">Name of the parameter.</param>
        /// <exception cref="T:System.ArgumentNullException"></exception>
        public static void ThrowIfArgumentIsNull<T>(this T obj, string parameterName) where T : class
        {
            if ((object)obj == null)
                throw new ArgumentNullException(parameterName + " not allowed to be null");
        }

        /// <summary>
        /// To the data table with type name of <paramref name="entity" />.
        /// </summary>
        /// <typeparam name="T">The entity type.</typeparam>
        /// <param name="entity">The entity.</param>
        /// <returns>An empty data table with type name of <paramref name="entity" />.</returns>
        public static DataTable ToDataTable<T>(this T entity)
        {
            Type componentType = typeof(T);
            DataTable dataTable = new DataTable(componentType.Name);
            foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(componentType))
            {
                Type type1 = Nullable.GetUnderlyingType(property.PropertyType);
                if ((object)type1 == null)
                    type1 = property.PropertyType;
                Type type2 = type1;
                dataTable.Columns.Add(property.Name, type2);
            }
            return dataTable;
        }

        /// <summary>Convertables the specified type.</summary>
        /// <param name="obj">The object.</param>
        /// <param name="type">The type.</param>
        /// <returns></returns>
        public static bool Convertable(this object obj, ObjectConvertbleSupportedType type)
        {
            if (obj.IsNull())
                return false;
            try
            {
                switch (type)
                {
                    case ObjectConvertbleSupportedType.Int:
                        int result1;
                        return int.TryParse(obj.ToString(), out result1);
                    case ObjectConvertbleSupportedType.Decimal:
                        Decimal result2;
                        return Decimal.TryParse(obj.ToString(), out result2);
                    case ObjectConvertbleSupportedType.DateTime:
                        DateTime result3;
                        return DateTime.TryParse(obj.ToString(), out result3);
                    default:
                        return false;
                }
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        /// <summary>Hiddens the field value.</summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj">The object.</param>
        /// <param name="isReverse">If true reverse the fileds in <typeparamref name="T" />.</param>
        /// <param name="fileds">The fileds.</param>
        /// <returns></returns>
        public static T HiddenFieldValue<T>(this T obj, bool isReverse = false, params string[] fileds) where T : class
        {
            if (obj.IsNull())
                return obj;
            foreach (PropertyInfo property in obj.GetType().GetProperties())
            {
                bool flag = isReverse ? !((IEnumerable<string>)fileds).Contains<string>(property.Name) : ((IEnumerable<string>)fileds).Contains<string>(property.Name);
                if (((!property.IsNotNull() ? 0 : (property.CanWrite ? 1 : 0)) & (flag ? 1 : 0)) != 0)
                    property.SetValue((object)obj, (object)null, (object[])null);
            }
            return obj;
        }
    }

    [Serializable]
    public enum ObjectConvertbleSupportedType
    {
        Int,
        Decimal,
        DateTime,
    }
}